feat(input): add macro language — repeat, literals, delays, holds, raw text#37
Conversation
…w text
Extends the input macro grammar inside `{}` braces:
- Repeat: `{a*5}`, `{enter*3}`, `{ctrl+c*2}`
- Quoted literal: `{"hello world"*2}` — braces-safe, `\"` escapes the quote
- Verb literal: `{text:content*N}` — same semantics as quoted form
- Delay pass-through: `{delay:N}` forwarded to core emitter as a brace token
- Hold/press/release pass-throughs: `{press:k}`, `{release:k}`, `{hold:k:dur}`
- Sigil sugar: `{_k}` (press), `{^k}` (release), `{~k:dur}` (hold)
- New command `input.text`: raw mode — no `{}`/`*` grammar, no adv-args, `\n`→`{enter}`, `\t`→`{tab}`
Safety caps: per-repeat max 1000, total expanded keys per macro max 5000;
both fail the command with a clear error rather than silently truncating.
`Command.String()` gains an `isInputRawCmd` case so `input.text` args
round-trip correctly (chars concatenated directly, no commas).
|
Warning Review limit reached
More reviews will be available in 2 hours, 15 minutes, and 3 seconds. Learn how PR review limits work. Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file). ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
📝 WalkthroughWalkthroughThis PR introduces a new ChangesInput Text and Macro Grammar
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
arguments.go (1)
135-141:⚠️ Potential issue | 🟠 Major | ⚡ Quick winCount expression tokens toward the macro length cap.
Line 140 appends expression output but never increments
totalLen, so repeated[expr]segments can bypassInputMacroMaxKeysenforcement.Proposed fix
case SymExpressionStart: exprValue, exprErr := sr.parseExpression() if exprErr != nil { return args, advArgs, exprErr } + totalLen++ + if totalLen > InputMacroMaxKeys { + return args, advArgs, ErrInputMacroTooLong + } args = append(args, exprValue) continue🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@arguments.go` around lines 135 - 141, The code handling SymExpressionStart appends exprValue but never updates totalLen, letting repeated [expr] segments bypass the InputMacroMaxKeys cap; after calling parseExpression and appending exprValue to args in the SymExpressionStart case, increment totalLen by the length of exprValue (use the same length metric used elsewhere, e.g., len(exprValue) or utf8.RuneCountInString if other branches use rune counts) and perform the same InputMacroMaxKeys check/error return as other token branches so the macro length cap is enforced for expression tokens as well.
🧹 Nitpick comments (1)
parser_input_macro_test.go (1)
50-443: 🏗️ Heavy liftConsolidate repeated parser scenarios into table-driven tests and assert with
cmp.Diff.This file currently uses many one-off tests with
assert.Equal/require.*; that conflicts with the repo’s_test.goguideline and makes scenario growth harder to maintain. Please convert repeated scenario blocks (backward-compat, repeats, literals, verbs, sigils, error-cases) into table-driven subtests and usecmp.Difffor comparison output.As per coding guidelines,
**/*_test.go: "Use table-driven tests for multiple scenarios" and "Usecmp.Difffrom google/go-cmp for detailed assertion output in tests".🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@parser_input_macro_test.go` around lines 50 - 443, Many one-off tests (e.g., TestInputMacro_BackwardCompat_*, TestInputMacro_Repeat_*, TestInputMacro_QuotedLiteral_*, TestInputMacro_TextVerb_*, TestInputText_*, and the error-case tests) should be consolidated into table-driven subtests: create slices of scenario structs (name, input, wantArgs, optionally wantErr or wantAdvArgs) and iterate with t.Run to exercise parseInputMacroArgs, parseInputTextArgs, or zapscript.NewParser as appropriate; replace assert.Equal checks with a cmp.Diff comparison and fail the subtest with t.Fatalf("mismatch (-want +got):\n%s", diff) when diff != "" (and keep require.NoError/require.Error checks for parse errors where needed but report mismatches with cmp.Diff), ensuring error-case scenarios assert the expected error values (e.g., zapscript.ErrInputMacroRepeatTooLarge, ErrInputMacroTooLong, ErrUnmatchedInputMacroExt, ErrInputMacroEmptyKey) and preserving existing caps/adv-arg checks; group tests by logical sections (backward-compat, repeats, quoted literals, text verb, input.text raw mode, sigils/hold verbs, safety caps, errors) and remove duplicated one-off test functions in favor of these table-driven subtests.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@reader.go`:
- Around line 180-186: The raw-mode branch in the
isInputRawCmd(normalizeCmdName(c.Name)) case writes args literally, which breaks
equivalence because parseInputRawArg maps newline/tab to "{enter}"/"{tab}";
change the loop that currently does for _, arg := range c.Args { _, _ =
b.WriteString(arg) } to serialize each arg using the same mapping used by
parseInputRawArg (i.e., replace '\n' with "{enter}" and '\t' with "{tab}" or
call/implement a small helper that mirrors parseInputRawArg's mapping) before
writing to b so reparsing preserves the original tokens.
---
Outside diff comments:
In `@arguments.go`:
- Around line 135-141: The code handling SymExpressionStart appends exprValue
but never updates totalLen, letting repeated [expr] segments bypass the
InputMacroMaxKeys cap; after calling parseExpression and appending exprValue to
args in the SymExpressionStart case, increment totalLen by the length of
exprValue (use the same length metric used elsewhere, e.g., len(exprValue) or
utf8.RuneCountInString if other branches use rune counts) and perform the same
InputMacroMaxKeys check/error return as other token branches so the macro length
cap is enforced for expression tokens as well.
---
Nitpick comments:
In `@parser_input_macro_test.go`:
- Around line 50-443: Many one-off tests (e.g., TestInputMacro_BackwardCompat_*,
TestInputMacro_Repeat_*, TestInputMacro_QuotedLiteral_*,
TestInputMacro_TextVerb_*, TestInputText_*, and the error-case tests) should be
consolidated into table-driven subtests: create slices of scenario structs
(name, input, wantArgs, optionally wantErr or wantAdvArgs) and iterate with
t.Run to exercise parseInputMacroArgs, parseInputTextArgs, or
zapscript.NewParser as appropriate; replace assert.Equal checks with a cmp.Diff
comparison and fail the subtest with t.Fatalf("mismatch (-want +got):\n%s",
diff) when diff != "" (and keep require.NoError/require.Error checks for parse
errors where needed but report mismatches with cmp.Diff), ensuring error-case
scenarios assert the expected error values (e.g.,
zapscript.ErrInputMacroRepeatTooLarge, ErrInputMacroTooLong,
ErrUnmatchedInputMacroExt, ErrInputMacroEmptyKey) and preserving existing
caps/adv-arg checks; group tests by logical sections (backward-compat, repeats,
quoted literals, text verb, input.text raw mode, sigils/hold verbs, safety caps,
errors) and remove duplicated one-off test functions in favor of these
table-driven subtests.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 03f59dcc-ca8c-4816-95b2-953a70c91e95
📒 Files selected for processing (8)
arguments.gocommand_string_test.gomodels.goparser.goparser_fuzz_test.goparser_input_macro_test.goreader.gosymbols.go
- reader.go: reverse {enter}/{tab} mappings in Command.String() raw-mode
serialization so round-trip parsing preserves the original tokens
- arguments.go: increment totalLen for SymExpressionStart tokens so
expression segments cannot bypass the InputMacroMaxKeys cap
- parser_input_macro_test.go: rewrite as table-driven subtests using
cmp.Diff following the project style; remove duplicates already covered
by parser_coverage_test.go; add wantAnyErr for unexported error types
Extends the input macro grammar (
input.keyboard,input.gamepad) with new forms inside{}braces, and adds a newinput.textraw-mode command.New grammar inside
{}{a*5}afive times{enter*3}{ctrl+c*2}{"hello"*2}hellotwice;\"escapes a quote{text:hello*2}{delay:500}{press:a},{hold:a:500},{release:a}{_a},{~a:500},{^a}New command:
input.text**input.text:free text here— the entire argument is literal; no{}/*grammar, no adv-args.\nmaps to{enter},\tmaps to{tab}. Useful for search strings, URLs, and virtual keyboard input.Safety caps
*Nceiling: 1000 —ErrInputMacroRepeatTooLargeErrInputMacroTooLongBoth fail the whole command with a clear error; no silent truncation.
Serialization
Command.String()gains anisInputRawCmdcase soinput.textround-trips correctly — per-character args are concatenated directly without commas.Summary by CodeRabbit
Release Notes
New Features
input.textcommand for raw text input without macro syntax processing.{key*N}), quoted literals, and pass-through verbs (delay,press,release,hold)._,^,~) in input macros.Tests